package negModel;

import java.io.IOException;
import java.util.Iterator;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import negModelExcep.*;

import org.apache.xerces.parsers.DOMParser;
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class Auto extends Vehiculo {

	private ListaPartes lp; 
	private Motor motor;
	private Transmision trans;
	private Rueda rueda;
	private String tipo;
	private double recorrido;
	private boolean acelerarApretado;
		
	public Auto (ListaPartes l1,String tipo){
		this.lp = new ListaPartes();
				
		Iterator it = l1.iterator();
		
		while (it.hasNext())
			this.agregarParte((Parte)it.next());
		
		this.recorrido = 0;
		this.acelerarApretado = false;
		this.tipo = tipo;
	}
	
	public Auto(Element xmlElement) {
		
		this.lp = new ListaPartes ();
		
		this.tipo=xmlElement.getAttribute("tipo");
		this.recorrido=Double.parseDouble(xmlElement.getAttribute("recorrido"));
		this.acelerarApretado=Boolean.parseBoolean(xmlElement.getAttribute("acelerarApretado"));
		
		Fabrica unaFabrica=new Fabrica();
		
		NodeList nodoslista=xmlElement.getElementsByTagName("AlistaParte");
		for(int nl=0;nl<nodoslista.getLength();nl++){
			NodeList nodos=xmlElement.getElementsByTagName("parte");
			for(int nc=0;nc<nodos.getLength();nc++){
				if (!((((Element)nodos.item(nc)).getAttribute("Clase").equals("llanta")) || (((Element)nodos.item(nc)).getAttribute("Clase").equals("neumatico")))) {

					Parte unaParte= unaFabrica.hacerParte(((Element)nodos.item(nc)).getAttribute("Clase"),(Element)nodos.item(nc));
					this.agregarParte(unaParte);
				}
			}
		}
	}
	
	public Element toXml(Document doc){
		Element xmlElement = doc.createElement("vehiculo");
		xmlElement.setAttribute("Clase","auto");
		
		xmlElement.setAttribute("tipo", this.tipo);;
		xmlElement.setAttribute("recorrido", ""+this.recorrido);;
		xmlElement.setAttribute("acelerarApretado", ""+this.acelerarApretado);;
				
		xmlElement.appendChild(this.lp.toXml(doc,"A"));
		
		return xmlElement;
	}
	
	public static void guardar(String nombreArchivo,Auto auto ) throws IOException, ParserConfigurationException{
	    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
	    DocumentBuilder loader = factory.newDocumentBuilder();
        Document doc = loader.newDocument();

        
        doc.appendChild(auto.toXml(doc));
        
        XMLSerializer serializer = new XMLSerializer();
        
        OutputFormat outFormat = new OutputFormat();

       
        outFormat.setVersion("1.0");
        outFormat.setIndenting(true);
        outFormat.setIndent(4);
        
        serializer.setOutputFormat(outFormat);
        serializer.setOutputCharStream(
          new java.io.FileWriter(nombreArchivo));
        serializer.serialize(doc);
	}
	
	public static Auto cargar (String nombreArchivo) throws IOException, ClassNotFoundException	{
		DOMParser parser = new DOMParser();

		try {
		    parser.parse(nombreArchivo);

		} catch (SAXException se) {
		    se.printStackTrace();
		} catch (IOException ioe) {
		    ioe.printStackTrace();
		}

		Document docXml = parser.getDocument();
		
		return new Auto(docXml.getDocumentElement());

	}
	
	
	public Parte agregarParte(Parte parte) {
		
		Parte parteDevuelta = null;
		parte.agregarMejora(this);
		
		try{
			parteDevuelta = (Parte)lp.reemplazarParte(parte);
			parteDevuelta.sacarMejora(this);
		}
		catch(eParteInexistente e){
			lp.add(parte);
		}
		
		return parteDevuelta;
	}
	
	public Parte sacarParte(Parte parte) throws eParteNoRemovible, eParteInexistente {
		
		Parte unaParte=null;
		
		if (!parte.esRemovible())
			throw new eParteNoRemovible();
		else 
			unaParte =(Parte)lp.borrarParte(parte);
		
		unaParte.sacarMejora(this);
		return unaParte;
	}
	
	public ListaPartes mostrarPartes(){
		
		ListaPartes auxlp = new ListaPartes();
		Iterator it = this.iterator();
		
		while(it.hasNext())
			auxlp.add((Parte)it.next());
		
		return auxlp;
	}
	
	private double obtenerFuncionalidadDePartes() throws eParteNoFunciona{
		
		Parte unaParte;
		Iterator it = this.iterator();
		double temp=1;
		
		while(it.hasNext()){
			unaParte = (Parte)it.next();
			
			temp *= unaParte.funcionar();
		}
		
		return temp;
	}
	
	public void avanzar (double coef){
		
		double ace=1/coef;
				
		try{
			ace*= this.obtenerFuncionalidadDePartes();
			recorrido += (ace*rueda.getCoeficiente());
			
		}
		catch(eParteNoFunciona e){
			this.desacelerar();
			this.desacelerar();
			if (this.motor.getRpm() == 0)
				this.recorrido += 0;
					
		}finally {
			ActualizarObservadores();
		}
	}
	// (==)
	private void setearCambio(){
		
		if(!(trans.esManual())){
			this.trans.setCambio(motor.getRpm(), this.getVelMaxima());
		}
	}
	
	public void acelerar(){
		
		motor.aumentarRpm(this.getVelMaxima());
		this.setearCambio();
		ActualizarObservadores();
							
	}
	
	public void desacelerar(){
		
		motor.disminuirRpm();
		this.setearCambio();
		ActualizarObservadores();
				
	}
	
	public double getRecorrido(){
		Double d = new Double(recorrido);
		return d.longValue();
	}
	
	public int mostrarEstadoDeTransmision(){
		return trans.getCambio();
	}
	
	public void subirMarcha(){
		trans.subirCambio();
		ActualizarObservadores();
	}
	
	public void bajarMarcha(){
		trans.bajarCambio();
		ActualizarObservadores();
	}
	
	public double getVelocidad(){
		double vel;
		
		if (trans.getCambio() == 0)
			vel = 0;
		else
			vel = (motor.getRpm() / trans.getMaxCambios());
		
		return vel;
	}
	
	public double getRpm(){
		
		return (motor.getRpm());		
	}
	
	public Iterator iterator(){
		return lp.iterator();
	}
		
	public void ActualizarObservadores(){
	
		setChanged();
		notifyObservers();		
	}

	public AlgoPesos CargarCombustible(){
		
		Tanque untanque = new Tanque();
		untanque = (Tanque)lp.devolverParte(untanque.getClass());
		Double d = new Double((untanque.getCapacidad() - untanque.getCantComb()));
		AlgoPesos costo = new AlgoPesos(d.longValue());
		return costo;
	}
	
	public void setMotor(Motor motor) {
		
		this.motor = motor;
							
	}
	
	public void setTransmision(Transmision trans){
				
		this.trans = trans;
						
	}
	
	public void setRueda(Rueda rue){
		
		this.rueda = rue;
			
	}
	
	public void setPedales(boolean acelerarApretado){
		
		this.acelerarApretado = acelerarApretado;
		
	}

	public void evaluarPedales() {
		
		if (this.acelerarApretado)
			this.acelerar();
		else if (!this.acelerarApretado)
			this.desacelerar();
		
	}
	
	public String getTipo(){
		return this.tipo;
	}
	
	public void Resetear(){
		recorrido=0;
		motor.ResetearRpm();
		trans.setCambio(0);
	}

	
	
		
}
